﻿Option Strict On
Option Explicit On
Option Infer On

Imports System
Imports System.Collections.Generic
Imports System.AddIn
Imports System.ComponentModel
Imports System.Linq
Imports System.Xml.Linq
Imports System.Xml.XPath
Imports GenDotNet.Task.Common
Imports GenDotNet.LinqGenerator
Imports GenDotNet.Model.Common
Imports <xmlns:orm="http://kadgen.com/KADORM.xsd">
Imports <xmlns:s="http://kadgen.com/Select.xsd">

<AddIn("LinqGeneratorTest", _
    Version:="1.0.0.0")> _
Public Class LinqGeneratorGenDotNetOne
   Implements ILinqGenerator

   Private Class CacheSet
      Public hashValue As Int32
      Public xDocument As XDocument
      Public objectWrapperBaseData As GenInputMetadata.BaseData
      Public objectRoot As GenInputMetadata.RootData
      Public storedProcRoot As SpInputMetadata.RootData
      Public xApiFragment As XElement
      Public xDataFragment As XElement

      Public Sub New(ByVal hashValue As Int32, _
                     ByVal xDocument As XDocument, _
                     ByVal objectRoot As GenInputMetadata.RootData, _
                     ByVal storedProcRoot As SpInputMetadata.RootData, _
                     ByVal xApiFragment As XElement, _
                     ByVal xDataFragment As XElement)
         Me.hashValue = hashValue
         Me.xDocument = xDocument
         Me.objectRoot = objectRoot
         Me.storedProcRoot = storedProcRoot
         Me.xApiFragment = xApiFragment
         Me.xDataFragment = xDataFragment
      End Sub

      Public Function BuildReturnElement() As XElement
         Return <s:Root>
                   <%= objectRoot.GetXList() %>
                   <%= xApiFragment %>
                   <%= xDataFragment %>
                   <%= storedProcRoot.GetXList %>
                </s:Root>
      End Function
   End Class

   Private mCache As New Dictionary(Of Int32, CacheSet)

#Region "Add-In Methods"

   Public Function SetXmlDocument( _
            ByVal inputXml As String) _
            As ISetupReturn _
            Implements ILinqGenerator.SetXmlDocument

      If String.IsNullOrEmpty(inputXml) Then
         Return Nothing
      End If
      Dim cacheSet As CacheSet = Nothing
      Dim hash = inputXml.GetHashCode()
      If mCache.TryGetValue(hash, cacheSet) Then
         Return New SetupReturn(hash, cacheSet.BuildReturnElement.ToString())
      End If
      Dim xDoc = XDocument.Parse(inputXml)
      cacheSet = New CacheSet(hash, xDoc, _
                              GenInputMetadata.RootData.LoadData(xDoc), _
                              SpInputMetadata.RootData.LoadData(xDoc), _
                              Nothing, Nothing)

      Console.WriteLine("Return Xml: " & (DateTime.Now.Ticks \ 1000).ToString)
      Return New SetupReturn(hash, cacheSet.BuildReturnElement().ToString())
   End Function

   'Dim ret = <s:Root>
   '             <s:Objects>
   '                <%= From obj In XDocument...<orm:Object> _
   '                   Select <s:Object <%= obj.Attributes %>/> %>
   '             </s:Objects>
   '             <s:Apis>
   '                <%= From api In XDocument...<orm:Api> _
   '                   Select <s:Api <%= api.Attributes %>/> %>
   '             </s:Apis>
   '             <s:Tables>
   '                <%= From table In XDocument...<orm:Table> _
   '                   Select <s:Table <%= table.Attributes %>/> %>
   '             </s:Tables>
   '             <s:StoredProcs>
   '                <%= From sp In XDocument...<orm:RunSP> _
   '                   Where sp.@Task.EqualsIgnoreCase("Create") _
   '                   Select <s:CreateStoredProc <%= sp.Attributes() %>/> %>
   '                <%= From sp In XDocument...<orm:RunSP> _
   '                   Where sp.@Task.EqualsIgnoreCase("Update") _
   '                   Select <s:UpdateStoredProc <%= sp.Attributes() %>/> %>
   '                <%= From sp In XDocument...<orm:RunSP> _
   '                   Where sp.@Task.EqualsIgnoreCase("Delete") _
   '                   Select <s:DeleteStoredProc <%= sp.Attributes() %>/> %>
   '                <%= From sp In XDocument...<orm:RunSP> _
   '                   Where sp.@Task.EqualsIgnoreCase("Retrieve") _
   '                   Select <s:RetrieveStoredProc <%= sp.Attributes() %>/> %>
   '                <%= From sp In XDocument...<orm:RunSP> _
   '                   Where sp.@Task.EqualsIgnoreCase("RetrieveChild") _
   '                   Select <s:RetrieveChildrenStoredProc <%= sp.Attributes() %>/> %>
   '                <%= From sp In XDocument...<orm:RunSP> _
   '                   Where sp.@Task.EqualsIgnoreCase("RetrieveHierarchy") _
   '                   Select <s:RetrieveHierarchyStoredProc <%= sp.Attributes() %>/> %>
   '                <%= From sp In XDocument...<orm:RunSP> _
   '                   Where sp.@Task.EqualsIgnoreCase("SetSelect") _
   '                   Select <s:RetrieveSetProc <%= sp.Attributes() %>/> %>
   '             </s:StoredProcs>
   '          </s:Root>.ToString()

   Public Sub ClearXmlDocument() _
      Implements LinqGenerator.ILinqGenerator.ClearXmlDocument
      'xDocument = Nothing
   End Sub

   'Public Function GetFileName( _
   '         ByVal name As String, _
   '         ByVal category As GeneratorCategory, _
   '         ByVal assemblyFileName As String, _
   '         ByVal templateName As String) _
   '         As String Implements _
   '         LinqGenerator.ILinqGenerator.GetFileName
   '   Dim template = GetTemplate(assemblyFileName, templateName)
   '   Return GetFileName(name, GetCategoryString(category), template)
   'End Function

   Public Function Generate( _
            ByVal name As String, _
            ByVal category As GeneratorCategory, _
            ByVal assemblyFileName As String, _
            ByVal templateName As String, _
            ByVal token As Int32) _
            As IGenerateReturn _
            Implements ILinqGenerator.Generate
      Return GetReturn(name, category, 0, assemblyFileName, templateName)
   End Function

   'Public Function GetOutput( _
   '         ByVal name As String, _
   '         ByVal category As GeneratorCategory, _
   '         ByVal assemblyFileName As String, _
   '         ByVal templateName As String) _
   '         As String _
   '         Implements ILinqGenerator.GetOutput
   '   Return GetOutput(name, GetCategoryString(category), assemblyFileName, templateName)
   'End Function

   'Public Function Generate( _
   '         ByVal name As String, _
   '         ByVal categoryString As String, _
   '         ByVal assemblyFileName As String, _
   '         ByVal templateName As String) _
   '         As IGenerateReturn _
   '         Implements ILinqGenerator.Generate
   '   'Console.WriteLine("Loaded Secondary app domain")
   '   'Dim element = (From x In xDocument...<Api> Where x.@Name = name).FirstOrDefault
   '   ' The zero is temporary
   '   Dim category = CType(System.Enum.Parse(GetType(GeneratorCategory), categoryString), GeneratorCategory)
   '   Return GetReturn(name, category, 0, assemblyFileName, templateName)
   'End Function

   'Public Function GetOutput( _
   '         ByVal name As String, _
   '         ByVal category As String, _
   '         ByVal assemblyFileName As String, _
   '         ByVal templateName As String) _
   '         As String _
   '         Implements ILinqGenerator.GetOutput
   '   'Console.WriteLine("Loaded Secondary app domain")
   '   'Dim element = (From x In xDocument...<orm:RunSP> Where x.@Name = name).FirstOrDefault
   '   Return GetTemplateOutput(GetElement(name, category), assemblyFileName, templateName)
   'End Function


#End Region

#Region "Private methods and properties"
   'Private Function GetElement( _
   '         ByVal name As String, _
   '         ByVal category As String) _
   '         As XElement
   '   Dim elementName = category
   '   If category.EndsWithIgnoreCase("StoredProc") Then
   '      elementName = "RunSP"
   '   End If
   '   ' TODO: THis must be moved across teh metadata boundary as this class should have no dependency on file structure
   '   Return (From x In xDocument.Descendants(GetXmlNamespace(orm) + elementName) _
   '                  Where x.@Name = name).FirstOrDefault
   'End Function

   'Private Function GetFileName( _
   '         ByRef name As String, _
   '         ByVal category As String, _
   '         ByVal template As ITemplate) _
   '         As String
   '   Dim element = GetElement(name, category)
   '   Return GetFileName(element, template)
   'End Function

   'Private Function GetFileName( _
   '         ByRef element As XElement, _
   '         ByVal template As ITemplate) _
   '         As String
   '   Return template.GetFileName(element)
   'End Function

   'Private Function GetTemplateOutput( _
   '        ByVal element As XElement, _
   '        ByVal assemblyFileName As String, _
   '        ByVal templateName As String, _
   '        ByRef fileName As String) _
   '        As String
   '   If element IsNot Nothing Then
   '      Dim template = GetTemplate(assemblyFileName, templateName)
   '      Return GetTemplateOutput(element, template, fileName)
   '   End If
   '   Return Nothing
   'End Function

   Private Function GetTemplateOutput( _
           ByVal rootdata As IRootData, _
           ByVal name As String, _
           ByVal template As ITemplate, _
           ByRef fileName As String) _
           As String
      If template IsNot Nothing Then
         Return template.Generate(rootdata, name, fileName)
      End If
      Return Nothing
   End Function

   Private Function GetReturn( _
             ByVal name As String, _
             ByVal category As GeneratorCategory, _
             ByVal token As Int32, _
             ByVal assemblyFileName As String, _
             ByVal templateName As String) _
             As IGenerateReturn
      Dim template = GetTemplate(assemblyFileName, templateName)
      Dim fileName As String = String.Empty
      Dim rootData As IRootData
      Dim cacheset As CacheSet
      If token <= 0 Then
         cacheset = mCache.Values(0)
      Else
         cacheset = mCache(token)
      End If
      Select Case category
         Case GeneratorCategory.Object
            rootData = cacheset.objectRoot
         Case GeneratorCategory.Api
            Throw New NotImplementedException("This is a placeholder for future API generation")
         Case GeneratorCategory.Table
            Throw New NotImplementedException("This is a placeholder for future database generation")
         Case Else
            rootData = cacheset.storedProcRoot
      End Select
      Dim output = GetTemplateOutput(rootData, name, template, fileName)
      Return New GenerateReturn(output, fileName)
   End Function

   Private Function GetTemplate( _
            ByVal assemblyFileName As String, _
            ByVal templateName As String) _
            As ITemplate
      Dim asm As Reflection.Assembly = Nothing
      If Not String.IsNullOrEmpty(assemblyFileName) Then
         asm = Reflection.Assembly.LoadFrom(assemblyFileName)
         Dim type As Type = Nothing
         If asm IsNot Nothing And Not String.IsNullOrEmpty(templateName) Then
            type = GetSpecifiedType(asm, templateName)
            If type IsNot Nothing Then
               Dim obj = Activator.CreateInstance(type)
               Return TryCast(obj, ITemplate)
            End If
         End If
      End If
      Return Nothing
   End Function

   Private Shared Function GetSpecifiedType( _
            ByVal asm As Reflection.Assembly, _
            ByVal name As String) _
            As System.Type
      Dim type As System.Type = Nothing
      ' This is tempoary to test a fix

      Try
         Dim types = asm.GetTypes()
      Catch ex As Exception
         Console.WriteLine(ex)
      End Try

      ' Kind of ugly but it works
      For Each testType As System.Type In asm.GetTypes
         If testType.Name.EqualsIgnoreCase(name) Then
            type = testType
            Exit For
         End If
      Next
      Return type
   End Function

   Private Shared Function GetCategoryString(ByVal category As GeneratorCategory) As String
      Dim ret As String = category.ToString()
      If ret.EqualsIgnoreCase("StoredProc") Then
         ret = "RunSP"
      End If
      Return ret
   End Function
#End Region


End Class
